<template>
    <!-- 时间选择区域 -->
    <div class="time-echarts">
        <!-- 使用条件渲染和动态组件替代 teleport，兼容 Vue 2 和 Vue 3 -->
        <component :is="portalComponent" to="body" v-if="hasPortalSupport">
            <div
                v-show="selectedTimePlan || (dragState.isDragging && dragState.hasMoved)"
                class="time-tip"
                :style="{
                    left: `${tipPosition.x}px`,
                    top: `${tipPosition.y}px`,
                    position: 'fixed',
                    zIndex: tipZIndex,
                }"
            >
                {{ tipContent }}
            </div>
        </component>

        <!-- 不支持传送门的回退方案 -->
        <div class="tip-container" v-else>
            <div
                v-show="selectedTimePlan || (dragState.isDragging && dragState.hasMoved)"
                class="time-tip"
                :style="{
                    left: `${tipPosition.x}px`,
                    top: `${tipPosition.y}px`,
                    position: 'fixed',
                    zIndex: tipZIndex,
                }"
            >
                {{ tipContent }}
            </div>
        </div>

        <div class="contents-month">
            <div class="bottom">
                <div class="leftbar">
                    <div
                        v-for="day in monthDays"
                        :key="day"
                        class="monthday"
                        :style="monthdayStyle"
                    >
                        <span class="day-number">{{ day }}</span>
                        <span class="day-unit">号</span>
                    </div>
                </div>
                <div class="right-month" ref="rightMonthRef" :style="rightMonthStyle">
                    <div class="grid-container">
                        <div v-for="i in totalGrids" :key="i" class="grid-column"></div>
                    </div>
                    <canvas
                        ref="canvasRef"
                        :style="{
                            cursor: getCursorStyle(),
                            pointerEvents: 'auto',
                        }"
                    ></canvas>
                </div>
            </div>
            <div class="bottomhour-month">
                <ul class="ul-month">
                    <li
                        v-for="hour in hours"
                        :key="hour"
                        :style="{ width: `${smallestUnitMonth}px` }"
                    >
                        {{ hour }}
                    </li>
                </ul>
            </div>
        </div>
    </div>
</template>

<script lang="ts" setup>
import { isVue2 } from 'vue-demi';
import { computed, defineProps, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue-demi';

import dayjs from 'dayjs';

// 类型定义
interface ParsedTime {
    day: string;
    hours: number;
    minutes: number;
}

interface TimeSegment {
    x: number;
    y: number;
    width: number;
}

interface DragState {
    isDragging: boolean;
    startX: number;
    startY: number;
    startRow: number; // 记录开始拖拽的行（月份中的第几天）
    isMovingBlock: boolean;
    movingStartX: number;
    movingStartY: number;
    selectedBlockStartX: number;
    selectedBlockWidth: number;
    selectedBlockStartRow: number;
    canMoveBlock: boolean;
    hasMoved: boolean;
    isResizing: boolean;
    resizeDirection: 'left' | 'right' | null;
    clickedPlan: { startDate: string; endDate: string } | null;
}

const props = defineProps<{
    timePlan: Array<{ startDate: string; endDate?: string }>;
    isViewMonthPlan?: boolean;
}>();

const emit = defineEmits(['timeSelect']);

// 常量定义
const MAX_BLOCK_HEIGHT = 40;

// refs
const rightMonthRef = ref<HTMLDivElement | null>(null);
const canvasRef = ref<HTMLCanvasElement | null>(null);
const smallestUnitMonth = ref(0);
const timeToPixels = ref(0);

// Portal 组件检测
const portalComponent = ref('div');
const hasPortalSupport = ref(false);

// 添加动态 z-index 值
const tipZIndex = ref(1000);

// 获取页面最大 z-index 值的函数
const getMaxZIndex = (): number => {
    let maxZIndex = 0;
    const allElements = document.getElementsByTagName('*');
    for (let i = 0; i < allElements.length; i++) {
        const element = allElements[i];
        const style = window.getComputedStyle(element);
        const zIndex = parseInt(style.zIndex, 10);
        if (!isNaN(zIndex) && zIndex > maxZIndex) {
            maxZIndex = zIndex;
        }
    }
    return maxZIndex;
};

// 更新 tip 的 z-index 值
const updateTipZIndex = () => {
    tipZIndex.value = getMaxZIndex() + 1;
};

// 计算属性
const blockHeight = computed(() =>
    smallestUnitMonth.value > MAX_BLOCK_HEIGHT ? MAX_BLOCK_HEIGHT : smallestUnitMonth.value,
);

const monthdayStyle = computed(() => ({
    height: `${blockHeight.value}px`,
    lineHeight: `${blockHeight.value}px`,
}));

// 1.获取每个月的天数
function getDaysInMonth(year: number, month: number) {
    let date = dayjs(`${year}-${month}-01`);
    let lastDay = date.endOf('month');
    return lastDay.date();
}
// 2.调用函数
const currentDate = new Date();
let year = currentDate.getFullYear();
let month = currentDate.getMonth() + 1;
let days = getDaysInMonth(year, month);
const monthDays = Array.from({ length: days }, (_, i) => i + 1);

const rightMonthStyle = computed(() => ({
    height: `${blockHeight.value * days}px`,
}));

const totalGrids = computed(() => 24 * days);

const validTimePlans = computed(() => props.timePlan.filter(isValidEntry));

const isValidDateString = (dateStr: string): boolean => {
    if (!dateStr) return false;
    const [day, time] = dateStr.split('-');
    if (!day || !time) return false;
    const [hours, minutes] = time.split(':').map(Number);
    return !isNaN(Number(day)) && !isNaN(Number(hours)) && !isNaN(Number(minutes));
};
const isValidEntry = (entry: { startDate: string; endDate?: string }): boolean => {
    return Boolean(
        entry.startDate &&
            entry.endDate &&
            isValidDateString(entry.startDate) &&
            isValidDateString(entry.endDate),
    );
};
let resizeObserver: ResizeObserver | null = null;
const hours = Array.from({ length: 24 }, (_, i) => i);

const calculateCellWidth = () => {
    if (rightMonthRef.value) {
        const rightDayWidth = rightMonthRef.value.clientWidth;
        smallestUnitMonth.value = parseFloat((rightDayWidth / 24).toFixed(2));
        timeToPixels.value = smallestUnitMonth.value / 4;
    }
};

const calculateSegments = (
    startDate: string,
    endDate?: string,
): { x: number; width: number; y: number }[] => {
    if (!startDate || !endDate) return [];

    const startParts = startDate.split('-');
    const startDay = Number(startParts[0]);
    const [startHour, startMinute] = startParts[1].split(':').map(Number);

    const endParts = endDate.split('-');
    const endDay = Number(endParts[0]);
    const [endHour, endMinute] = endParts[1].split(':').map(Number);

    const segments: { x: number; width: number; y: number }[] = [];

    // 处理同一天的情况
    if (startDay === endDay) {
        const x = (startHour + startMinute / 60) * smallestUnitMonth.value;
        const width =
            (endHour + endMinute / 60 - (startHour + startMinute / 60)) * smallestUnitMonth.value;
        const y = (startDay - 1) * blockHeight.value;
        segments.push({ x, y, width });
    } else {
        // 处理起始天
        const startX = (startHour + startMinute / 60) * smallestUnitMonth.value;
        const startWidth = (24 - (startHour + startMinute / 60)) * smallestUnitMonth.value;
        segments.push({
            x: startX,
            y: (startDay - 1) * blockHeight.value,
            width: startWidth,
        });

        // 处理中间的完整天
        for (let day = startDay + 1; day < endDay; day++) {
            segments.push({
                x: 0,
                y: (day - 1) * blockHeight.value,
                width: 24 * smallestUnitMonth.value,
            });
        }

        // 处理结束天
        const endWidth = (endHour + endMinute / 60) * smallestUnitMonth.value;
        segments.push({
            x: 0,
            y: (endDay - 1) * blockHeight.value,
            width: endWidth,
        });
    }

    return segments;
};

const drawTimeBlocks = (
    isPreview: boolean = false,
    segment?: { startDate: string; endDate: string },
) => {
    const container = rightMonthRef.value;
    const gridContainer = container?.querySelector('.grid-container');
    const canvas = canvasRef.value;
    if (!container || !gridContainer || !canvas) return;

    const containerWidth = gridContainer.clientWidth;
    const containerHeight = container.clientHeight;

    canvas.width = containerWidth;
    canvas.height = containerHeight;

    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 分离选中的时间块和非选中的时间块
    const selectedPlan = selectedTimePlan.value;
    const nonSelectedPlans = validTimePlans.value.filter(
        (entry) =>
            !selectedPlan ||
            entry.startDate !== selectedPlan.startDate ||
            entry.endDate !== selectedPlan.endDate,
    );

    // 先绘制所有非选中的时间块
    nonSelectedPlans.forEach((entry) => {
        if (!isValidEntry(entry)) return;

        const segments = calculateSegments(entry.startDate, entry.endDate);
        segments.forEach((segment) => {
            // 绘制时间块
            ctx.fillStyle = 'rgba(29, 185, 105, 0.3)';
            ctx.beginPath();

            // 绘制圆角矩形
            const radius = 4;
            const topPadding = 3; // 减小上边距
            const bottomPadding = 4; // 增加下边距

            ctx.moveTo(segment.x + radius, segment.y + topPadding);
            ctx.lineTo(segment.x + segment.width - radius, segment.y + topPadding);
            ctx.quadraticCurveTo(
                segment.x + segment.width,
                segment.y + topPadding,
                segment.x + segment.width,
                segment.y + topPadding + radius,
            );
            ctx.lineTo(
                segment.x + segment.width,
                segment.y + blockHeight.value - bottomPadding - radius,
            );
            ctx.quadraticCurveTo(
                segment.x + segment.width,
                segment.y + blockHeight.value - bottomPadding,
                segment.x + segment.width - radius,
                segment.y + blockHeight.value - bottomPadding,
            );
            ctx.lineTo(segment.x + radius, segment.y + blockHeight.value - bottomPadding);
            ctx.quadraticCurveTo(
                segment.x,
                segment.y + blockHeight.value - bottomPadding,
                segment.x,
                segment.y + blockHeight.value - bottomPadding - radius,
            );
            ctx.lineTo(segment.x, segment.y + topPadding + radius);
            ctx.quadraticCurveTo(
                segment.x,
                segment.y + topPadding,
                segment.x + radius,
                segment.y + topPadding,
            );

            ctx.fill();
        });
    });

    // 再绘制选中的时间块
    if (selectedPlan && isValidEntry(selectedPlan)) {
        const segments = calculateSegments(selectedPlan.startDate, selectedPlan.endDate);
        segments.forEach((segment) => {
            // 为选中的时间块添加高亮效果
            ctx.save();
            ctx.shadowColor = 'rgba(117, 211, 186, 0.6)';
            ctx.shadowBlur = 3;
            ctx.shadowOffsetX = 0;
            ctx.shadowOffsetY = 0;

            // 使用稍微不同的颜色突出显示选中的时间块
            ctx.fillStyle = 'rgba(29, 185, 105, 0.4)';
            ctx.beginPath();

            // 绘制圆角矩形
            const radius = 4;
            const topPadding = 3; // 减小上边距
            const bottomPadding = 4; // 增加下边距

            ctx.moveTo(segment.x + radius, segment.y + topPadding);
            ctx.lineTo(segment.x + segment.width - radius, segment.y + topPadding);
            ctx.quadraticCurveTo(
                segment.x + segment.width,
                segment.y + topPadding,
                segment.x + segment.width,
                segment.y + topPadding + radius,
            );
            ctx.lineTo(
                segment.x + segment.width,
                segment.y + blockHeight.value - bottomPadding - radius,
            );
            ctx.quadraticCurveTo(
                segment.x + segment.width,
                segment.y + blockHeight.value - bottomPadding,
                segment.x + segment.width - radius,
                segment.y + blockHeight.value - bottomPadding,
            );
            ctx.lineTo(segment.x + radius, segment.y + blockHeight.value - bottomPadding);
            ctx.quadraticCurveTo(
                segment.x,
                segment.y + blockHeight.value - bottomPadding,
                segment.x,
                segment.y + blockHeight.value - bottomPadding - radius,
            );
            ctx.lineTo(segment.x, segment.y + topPadding + radius);
            ctx.quadraticCurveTo(
                segment.x,
                segment.y + topPadding,
                segment.x + radius,
                segment.y + topPadding,
            );

            ctx.fill();

            // 绘制边框
            ctx.strokeStyle = '#75d3ba';
            ctx.lineWidth = 1.2;
            ctx.stroke();
            ctx.restore();
        });
    }

    // 如果是预览模式，再绘制预览时间块
    if (isPreview && segment) {
        const segments = calculateSegments(segment.startDate, segment.endDate);
        segments.forEach((segment) => {
            // 使用不同的颜色或透明度来区分预览时间块
            ctx.fillStyle = 'rgba(29, 185, 105, 0.2)'; // 使用更透明的颜色
            ctx.beginPath();

            // 绘制圆角矩形
            const radius = 3;
            const topPadding = 2; // 减小上边距
            const bottomPadding = 4; // 增加下边距

            ctx.moveTo(segment.x + radius, segment.y + topPadding);
            ctx.lineTo(segment.x + segment.width - radius, segment.y + topPadding);
            ctx.quadraticCurveTo(
                segment.x + segment.width,
                segment.y + topPadding,
                segment.x + segment.width,
                segment.y + topPadding + radius,
            );
            ctx.lineTo(
                segment.x + segment.width,
                segment.y + blockHeight.value - bottomPadding - radius,
            );
            ctx.quadraticCurveTo(
                segment.x + segment.width,
                segment.y + blockHeight.value - bottomPadding,
                segment.x + segment.width - radius,
                segment.y + blockHeight.value - bottomPadding,
            );
            ctx.lineTo(segment.x + radius, segment.y + blockHeight.value - bottomPadding);
            ctx.quadraticCurveTo(
                segment.x,
                segment.y + blockHeight.value - bottomPadding,
                segment.x,
                segment.y + blockHeight.value - bottomPadding - radius,
            );
            ctx.lineTo(segment.x, segment.y + topPadding + radius);
            ctx.quadraticCurveTo(
                segment.x,
                segment.y + topPadding,
                segment.x + radius,
                segment.y + topPadding,
            );

            ctx.fill();

            // 添加虚线边框以区分预览
            ctx.strokeStyle = 'rgba(29, 185, 105, 0.5)';
            ctx.lineWidth = 1;
            ctx.setLineDash([3, 3]); // 设置虚线样式
            ctx.stroke();
            ctx.setLineDash([]); // 重置虚线样式
        });
    }
};

// 维护本地的时间计划数组
const localTimePlan = ref<Array<{ startDate: string; endDate: string }>>([]);

// 添加状态来跟踪当前选中的时间块
const selectedTimePlan = ref<{ startDate: string; endDate: string } | null>(null);

const dragState = ref<DragState>({
    isDragging: false,
    startX: 0,
    startY: 0,
    startRow: 0,
    isMovingBlock: false,
    movingStartX: 0,
    movingStartY: 0,
    selectedBlockStartX: 0,
    selectedBlockWidth: 0,
    selectedBlockStartRow: 0,
    canMoveBlock: false,
    hasMoved: false,
    isResizing: false,
    resizeDirection: null,
    clickedPlan: null,
});

// 修改对 props.timePlan 的监听
watch(
    () => props.timePlan,
    (newValue) => {
        if (Array.isArray(newValue)) {
            // 更新本地数据
            localTimePlan.value = [...newValue];
            // 如果当前选中的时间段已被删除，清除选中状态
            if (selectedTimePlan.value) {
                const stillExists = newValue.some(
                    (plan) =>
                        plan.startDate === selectedTimePlan.value?.startDate &&
                        plan.endDate === selectedTimePlan.value?.endDate,
                );
                if (!stillExists) {
                    selectedTimePlan.value = null;
                    dragState.value.canMoveBlock = false;
                }
            }
            // 重新绘制时间块
            nextTick(() => {
                drawTimeBlocks();
            });
        }
    },
    { deep: true, immediate: true },
);

// 添加解析时间的函数
const parseTime = (timeStr: string): ParsedTime | null => {
    if (!timeStr) return null;
    const [day, time] = timeStr.split('-');
    if (!day || !time) return null;

    const [hours, minutes] = time.split(':').map(Number);
    if (isNaN(hours) || isNaN(minutes)) return null;

    return {
        day,
        hours,
        minutes,
    };
};

// 首先修改 findClickedTimePlan 函数，优先检查选中的时间块
const findClickedTimePlan = (x: number, y: number) => {
    const canvas = canvasRef.value;
    if (!canvas) return null;

    const gridWidth = canvas.width / 24;
    const rowHeight = canvas.height / days;

    // 首先检查当前选中的时间块，优先返回
    if (selectedTimePlan.value) {
        const startTime = parseTime(selectedTimePlan.value.startDate);
        const endTime = parseTime(selectedTimePlan.value.endDate);
        if (startTime && endTime) {
            const startRow = parseInt(startTime.day) - 1;
            const endRow = parseInt(endTime.day) - 1;
            const row = Math.floor(y / rowHeight);

            // 检查点击行是否在时间块范围内
            if (row >= startRow && row <= endRow) {
                let startHour = 0;
                let endHour = 24;

                // 对开始和结束行特殊处理
                if (row === startRow) {
                    startHour = startTime.hours + startTime.minutes / 60;
                }
                if (row === endRow) {
                    endHour = endTime.hours + endTime.minutes / 60;
                }

                // 检查点击位置是否在时间块的横向范围内
                const clickHour = x / gridWidth;
                if (clickHour >= startHour && clickHour <= endHour) {
                    return selectedTimePlan.value;
                }
            }
        }
    }

    // 然后检查其他时间块
    for (const plan of localTimePlan.value) {
        // 跳过当前选中的时间块，因为已经检查过了
        if (
            selectedTimePlan.value &&
            plan.startDate === selectedTimePlan.value.startDate &&
            plan.endDate === selectedTimePlan.value.endDate
        ) {
            continue;
        }

        const startTime = parseTime(plan.startDate);
        const endTime = parseTime(plan.endDate);
        if (startTime && endTime) {
            const startRow = parseInt(startTime.day) - 1;
            const endRow = parseInt(endTime.day) - 1;
            const row = Math.floor(y / rowHeight);

            // 检查点击行是否在时间块范围内
            if (row >= startRow && row <= endRow) {
                let startHour = 0;
                let endHour = 24;

                // 对开始和结束行特殊处理
                if (row === startRow) {
                    startHour = startTime.hours + startTime.minutes / 60;
                }
                if (row === endRow) {
                    endHour = endTime.hours + endTime.minutes / 60;
                }

                // 检查点击位置是否在时间块的横向范围内
                const clickHour = x / gridWidth;
                if (clickHour >= startHour && clickHour <= endHour) {
                    return plan;
                }
            }
        }
    }

    return null;
};

// 修改 checkResizeZone 函数，确保包含边界点
const checkResizeZone = (
    x: number,
    plan: { startDate: string; endDate: string },
    row?: number,
): 'left' | 'right' | null => {
    const gridWidth = canvasRef.value?.width ? canvasRef.value.width / 24 : 0;
    const startTime = parseTime(plan.startDate);
    const endTime = parseTime(plan.endDate);
    if (!startTime || !endTime) return null;

    const resizeZone = 10;

    // 如果提供了行信息，检查是否在正确的行上
    if (row !== undefined) {
        const startRow = parseInt(startTime.day) - 1;
        const endRow = parseInt(endTime.day) - 1;

        // 只在对应的行检查边缘
        if (row === startRow) {
            const startX = (startTime.hours + startTime.minutes / 60) * gridWidth;
            // 修改左侧检测逻辑：包含起始位置和向外延伸的区域
            if (Math.abs(x - startX) <= resizeZone) return 'left';
        }

        if (row === endRow) {
            const endX = (endTime.hours + endTime.minutes / 60) * gridWidth;
            // 修改右侧检测逻辑：包含结束位置和向外延伸的区域
            if (Math.abs(x - endX) <= resizeZone) return 'right';
        }
    } else {
        // 如果没有提供行信息，检查所有可能的边缘
        const startX = (startTime.hours + startTime.minutes / 60) * gridWidth;
        const endX = (endTime.hours + endTime.minutes / 60) * gridWidth;

        // 修改左侧检测逻辑：包含起始位置和向外延伸的区域
        if (Math.abs(x - startX) <= resizeZone) return 'left';
        // 修改右侧检测逻辑：包含结束位置和向外延伸的区域
        if (Math.abs(x - endX) <= resizeZone) return 'right';
    }

    return null;
};

const handleMouseDown = (e: MouseEvent) => {
    // 如果是查看模式，完全禁止任何交互，直接返回
    if (props.isViewMonthPlan) {
        return;
    }

    // 添加 user-select: none
    document.body.style.userSelect = 'none';

    const canvas = canvasRef.value;
    const rect = canvas?.getBoundingClientRect();
    if (!canvas || !rect) return;

    const clickX = e.clientX - rect.left;
    const clickY = e.clientY - rect.top;
    const clickedPlan = findClickedTimePlan(clickX, clickY);
    const clickedRow = Math.floor(clickY / blockHeight.value);

    // 明确重置hasMoved状态
    dragState.value.hasMoved = false;

    // 如果点击的是已选中的时间块，处理移动和调整大小
    if (
        clickedPlan &&
        selectedTimePlan.value &&
        clickedPlan.startDate === selectedTimePlan.value.startDate &&
        clickedPlan.endDate === selectedTimePlan.value.endDate
    ) {
        // 检查是否点击在边缘调整区域
        const resizeDirection = checkResizeZone(clickX, clickedPlan, clickedRow);
        if (resizeDirection) {
            dragState.value.isResizing = true;
            dragState.value.resizeDirection = resizeDirection;
            return;
        }

        // 设置移动状态
        dragState.value.isMovingBlock = true;
        dragState.value.movingStartX = clickX;
        dragState.value.movingStartY = clickY;
        dragState.value.canMoveBlock = true;

        const startTime = parseTime(clickedPlan.startDate);
        const endTime = parseTime(clickedPlan.endDate);
        if (startTime && endTime) {
            const gridWidth = canvas.width / 24;
            dragState.value.selectedBlockStartX =
                (startTime.hours + startTime.minutes / 60) * gridWidth;
            dragState.value.selectedBlockWidth =
                (endTime.hours +
                    endTime.minutes / 60 -
                    (startTime.hours + startTime.minutes / 60)) *
                gridWidth;
            dragState.value.selectedBlockStartRow = parseInt(startTime.day) - 1;
        }
        return;
    }

    // 记录点击的时间块，但不立即选中
    dragState.value.clickedPlan = clickedPlan;

    // 设置拖拽状态
    dragState.value.isDragging = true;
    dragState.value.startX = clickX;
    dragState.value.startY = clickY;
    dragState.value.startRow = Math.floor(clickY / blockHeight.value);
};

const drawRoundedRect = (
    ctx: CanvasRenderingContext2D,
    x: number,
    y: number,
    width: number,
    height: number,
    radius: number,
) => {
    ctx.beginPath();
    ctx.moveTo(x + radius, y);
    ctx.lineTo(x + width - radius, y);
    ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
    ctx.lineTo(x + width, y + height - radius);
    ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
    ctx.lineTo(x + radius, y + height);
    ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
    ctx.lineTo(x, y + radius);
    ctx.quadraticCurveTo(x, y, x + radius, y);
    ctx.closePath();
};

const handleMouseMove = (e: MouseEvent) => {
    // 如果是查看模式，完全禁止任何交互
    if (props.isViewMonthPlan) {
        // 只更新鼠标样式，不执行其他任何操作
        const canvas = canvasRef.value;
        if (canvas) {
            canvas.style.cursor = 'default';
        }
        return;
    }

    const canvas = canvasRef.value;
    const rect = canvas?.getBoundingClientRect();
    if (!canvas || !rect) return;

    const mouseX = e.clientX - rect.left;
    const mouseY = e.clientY - rect.top;
    const clickedRow = Math.floor(mouseY / (canvas.height / days));

    // 检查时间块的可见性
    // 只在进行交互性操作时检查可见性，减少不必要的计算
    if (selectedTimePlan.value && (dragState.value.isResizing || dragState.value.isMovingBlock)) {
        // 在交互过程中重新设置观察器
        observeSelectedTimeBlock();

        // 如果时间块已经变为不可见状态，立即取消操作
        if (!isSelectedBlockVisible.value) {
            // 取消操作状态
            dragState.value.isResizing = false;
            dragState.value.resizeDirection = null;
            dragState.value.isMovingBlock = false;
            dragState.value.canMoveBlock = false;
            // 重置选中状态
            resetTipPosition();
            selectedTimePlan.value = null;
            // 重绘时间块
            drawTimeBlocks();
            return;
        }
    }

    // 更新鼠标样式
    if (selectedTimePlan.value && !dragState.value.isMovingBlock && !dragState.value.isResizing) {
        const resizeDirection = checkResizeZone(mouseX, selectedTimePlan.value, clickedRow);
        canvas.style.cursor = resizeDirection ? 'ew-resize' : 'pointer';
    }

    // 处理调整大小
    if (dragState.value.isResizing && selectedTimePlan.value) {
        const gridWidth = canvas.width / 24;
        const quarterWidth = gridWidth / 4;
        const blockHeight = canvas.height / days;

        // 解析当前选中时间块的开始和结束时间
        const startTime = parseTime(selectedTimePlan.value.startDate);
        const endTime = parseTime(selectedTimePlan.value.endDate);
        if (!startTime || !endTime) return;

        // 计算新的时间位置
        if (dragState.value.resizeDirection === 'left') {
            // 调整开始时间
            const newStartQuarters = Math.round(mouseX / quarterWidth);
            let newStartHour = Math.floor(newStartQuarters / 4);
            let newStartMinutes = (newStartQuarters % 4) * 15;

            // 计算新的开始行（天）
            const newStartRow = Math.min(clickedRow, parseInt(endTime.day) - 1);
            let newStartDay = newStartRow + 1;

            // 确保新的开始时间不晚于结束时间
            const endHour = endTime.hours;
            const endMinutes = endTime.minutes;
            const endDay = parseInt(endTime.day);

            // 添加边界限制
            // 限制开始时间不能小于 00:00
            if (newStartHour < 0) {
                newStartHour = 0;
                newStartMinutes = 0;
            }

            // 确保开始日期不小于1
            if (newStartDay < 1) {
                newStartDay = 1;
            }

            // 确保开始日期不大于当月最大天数
            if (newStartDay > days) {
                newStartDay = days;
            }

            // 确保开始时间和结束时间至少相差15分钟
            if (newStartDay === endDay) {
                // 同一天的情况
                const endTotalMinutes = endHour * 60 + endMinutes;
                const newStartTotalMinutes = newStartHour * 60 + newStartMinutes;

                if (endTotalMinutes - newStartTotalMinutes < 15) {
                    // 如果差距小于15分钟，将开始时间设置为结束时间前15分钟
                    const adjustedTotalMinutes = endTotalMinutes - 15;
                    if (adjustedTotalMinutes >= 0) {
                        newStartHour = Math.floor(adjustedTotalMinutes / 60);
                        newStartMinutes = adjustedTotalMinutes % 60;
                    } else {
                        // 如果结束时间小于00:15，则设置开始时间为00:00
                        newStartHour = 0;
                        newStartMinutes = 0;
                    }
                }
            }

            // 检查是否超过了结束日期
            if (newStartDay > endDay) return; // 无效的天数范围

            // 格式化新的开始时间
            const newStartDate = `${newStartDay}-${newStartHour.toString().padStart(2, '0')}:${newStartMinutes.toString().padStart(2, '0')}`;

            // 更新时间块
            const index = localTimePlan.value.findIndex(
                (plan) =>
                    plan.startDate === selectedTimePlan.value?.startDate &&
                    plan.endDate === selectedTimePlan.value?.endDate,
            );

            if (index !== -1) {
                localTimePlan.value[index].startDate = newStartDate;
                selectedTimePlan.value = localTimePlan.value[index];
            }
        } else if (dragState.value.resizeDirection === 'right') {
            // 调整结束时间
            const newEndQuarters = Math.round(mouseX / quarterWidth);
            let newEndHour = Math.floor(newEndQuarters / 4);
            let newEndMinutes = (newEndQuarters % 4) * 15;

            // 计算新的结束行（天）
            const newEndRow = Math.max(clickedRow, parseInt(startTime.day) - 1);
            let newEndDay = newEndRow + 1;

            // 确保新的结束时间不早于开始时间
            const startHour = startTime.hours;
            const startMinutes = startTime.minutes;
            const startDay = parseInt(startTime.day);

            // 如果结束时间在当前天达到 00:00，检查是否需要处理特殊情况
            if (newEndHour <= 0 && newEndMinutes <= 0) {
                // 如果不是开始天，则允许设置为 00:00
                if (newEndDay > startDay) {
                    // 检查鼠标是否上移了一天
                    const originalEndDay = parseInt(endTime.day);
                    if (newEndRow < originalEndDay - 1) {
                        // 如果上移了一天，则跳到上一天的 00:00
                        newEndDay--;
                        newEndHour = 0;
                        newEndMinutes = 0;
                    } else {
                        // 如果没有上移，则保持在当前天的 00:00
                        newEndHour = 0;
                        newEndMinutes = 0;
                    }
                } else {
                    // 如果已经是开始天，确保结束时间至少比开始时间晚 15 分钟
                    const startTotalMinutes = startHour * 60 + startMinutes;
                    const minEndTotalMinutes = startTotalMinutes + 15;

                    newEndHour = Math.floor(minEndTotalMinutes / 60);
                    newEndMinutes = minEndTotalMinutes % 60;
                }
            }

            // 限制结束时间不能超过 23:59
            if (newEndHour > 23 || (newEndHour === 23 && newEndMinutes > 59)) {
                newEndHour = 23;
                newEndMinutes = 59;
            }

            // 确保结束日期不大于当月最大天数
            if (newEndDay > days) {
                newEndDay = days;
            }

            // 如果是同一天，确保开始时间和结束时间至少相差15分钟
            if (startDay === newEndDay) {
                const startTotalMinutes = startHour * 60 + startMinutes;
                const newEndTotalMinutes = newEndHour * 60 + newEndMinutes;

                if (newEndTotalMinutes - startTotalMinutes < 15) {
                    // 如果差距小于15分钟，将结束时间设置为开始时间后15分钟
                    const adjustedTotalMinutes = startTotalMinutes + 15;
                    if (adjustedTotalMinutes <= 24 * 60) {
                        newEndHour = Math.floor(adjustedTotalMinutes / 60);
                        newEndMinutes = adjustedTotalMinutes % 60;
                    } else {
                        // 如果开始时间大于23:45，则设置结束时间为23:59
                        newEndHour = 23;
                        newEndMinutes = 59;
                    }
                }
            }

            // 格式化新的结束时间
            const newEndDate = `${newEndDay}-${newEndHour.toString().padStart(2, '0')}:${newEndMinutes.toString().padStart(2, '0')}`;

            // 更新时间块
            const index = localTimePlan.value.findIndex(
                (plan) =>
                    plan.startDate === selectedTimePlan.value?.startDate &&
                    plan.endDate === selectedTimePlan.value?.endDate,
            );

            if (index !== -1) {
                localTimePlan.value[index].endDate = newEndDate;
                selectedTimePlan.value = localTimePlan.value[index];
            }
        }

        // 更新 tip 位置
        updateTip();
        // 重绘时间块
        drawTimeBlocks();
        // 发送更新事件到父组件
        emit('timeSelect', localTimePlan.value);
        return;
    }

    // 处理时间块的移动
    if (dragState.value.isMovingBlock && selectedTimePlan.value) {
        const currentX = e.clientX - rect.left;
        const currentY = e.clientY - rect.top;
        const deltaX = currentX - dragState.value.movingStartX;
        const deltaY = currentY - dragState.value.movingStartY;

        const gridWidth = canvas.width / 24;
        const quarterWidth = gridWidth / 4;
        const rowHeight = canvas.height / days;

        // 检查是否是跨天时间段
        const startTime = parseTime(selectedTimePlan.value.startDate);
        const endTime = parseTime(selectedTimePlan.value.endDate);
        if (!startTime || !endTime) return;

        const isMultiDay = startTime.day !== endTime.day;

        // 计算新的垂直位置（天数）
        let newStartRow = Math.max(
            0,
            Math.min(
                days - 1,
                Math.round(dragState.value.selectedBlockStartRow + deltaY / rowHeight),
            ),
        );

        if (isMultiDay) {
            // 跨天时间段只能上下移动
            const dayDiff = parseInt(endTime.day) - parseInt(startTime.day);
            let newEndRow = newStartRow + dayDiff;

            // 检查是否超出边界，如果超出则调整开始行位置
            if (newEndRow >= days) {
                newStartRow = Math.max(0, days - 1 - dayDiff);
                newEndRow = newStartRow + dayDiff;
            }

            // 格式化新的时间
            const newStartDate = `${newStartRow + 1}-${startTime.hours.toString().padStart(2, '0')}:${startTime.minutes.toString().padStart(2, '0')}`;
            const newEndDate = `${newEndRow + 1}-${endTime.hours.toString().padStart(2, '0')}:${endTime.minutes.toString().padStart(2, '0')}`;

            // 找到当前选中时间块在数组中的索引
            const index = localTimePlan.value.findIndex(
                (plan) =>
                    plan.startDate === selectedTimePlan.value?.startDate &&
                    plan.endDate === selectedTimePlan.value?.endDate,
            );

            if (index !== -1) {
                // 更新本地数组中的时间块
                localTimePlan.value[index].startDate = newStartDate;
                localTimePlan.value[index].endDate = newEndDate;

                // 更新选中的时间块
                selectedTimePlan.value = localTimePlan.value[index];

                // 获取容器和画布的位置信息
                const timeEcharts = canvas.closest('.time-echarts');
                if (timeEcharts) {
                    const containerRect = timeEcharts.getBoundingClientRect();
                    const canvasRect = canvas.getBoundingClientRect();

                    // 非跨天情况 - 直接计算同一天内的中点
                    const startRow = newStartRow;
                    const startHour = startTime.hours + startTime.minutes / 60;
                    const endHour = endTime.hours + endTime.minutes / 60;

                    // 计算水平中点（时间轴上的中点）
                    const middleX = ((startHour + endHour) / 2) * gridWidth;

                    // 非跨天的垂直位置就是该行的中点
                    const middleY = (startRow + 0.5) * rowHeight; // 加0.5表示行的中点

                    // 更新 tip 位置，使用相对于容器的位置
                    tipPosition.value = {
                        x: canvasRect.left - containerRect.left + middleX,
                        y: canvasRect.top - containerRect.top + middleY,
                    };
                }
            }
        } else {
            // 非跨天时间段可以左右和上下移动
            // 计算时间块的持续时间（小时）
            const duration =
                endTime.hours + endTime.minutes / 60 - (startTime.hours + startTime.minutes / 60);

            // 计算新的水平位置（15分钟刻度）
            const newStartQuarters = Math.round(
                (dragState.value.selectedBlockStartX + deltaX) / quarterWidth,
            );
            let newStartHour = Math.floor(newStartQuarters / 4);
            let newStartMinutes = (newStartQuarters % 4) * 15;

            // 边界处理：如果新的开始时间小于 0，强制设为 0
            if (newStartHour < 0 || (newStartHour === 0 && newStartMinutes < 0)) {
                newStartHour = 0;
                newStartMinutes = 0;
            }

            // 计算新的结束时间
            let newEndHour = newStartHour + Math.floor(duration);
            let newEndMinutes = newStartMinutes + Math.round((duration % 1) * 60);

            // 处理分钟进位
            if (newEndMinutes >= 60) {
                newEndHour += Math.floor(newEndMinutes / 60);
                newEndMinutes = newEndMinutes % 60;
            }

            // 如果结束时间超过 23:59，调整开始时间
            if (newEndHour > 23 || (newEndHour === 23 && newEndMinutes > 59)) {
                newEndHour = 23;
                newEndMinutes = 59;
                // 反向计算新的开始时间
                newStartHour = newEndHour - Math.floor(duration);
                newStartMinutes = newEndMinutes - Math.round((duration % 1) * 60);
                if (newStartMinutes < 0) {
                    newStartHour--;
                    newStartMinutes += 60;
                }
            }

            // 格式化新的时间
            const newStartDate = `${newStartRow + 1}-${newStartHour.toString().padStart(2, '0')}:${newStartMinutes.toString().padStart(2, '0')}`;
            const newEndDate = `${newStartRow + 1}-${newEndHour.toString().padStart(2, '0')}:${newEndMinutes.toString().padStart(2, '0')}`;

            // 找到当前选中时间块在数组中的索引
            const index = localTimePlan.value.findIndex(
                (plan) =>
                    plan.startDate === selectedTimePlan.value?.startDate &&
                    plan.endDate === selectedTimePlan.value?.endDate,
            );

            if (index !== -1) {
                // 更新本地数组中的时间块
                localTimePlan.value[index].startDate = newStartDate;
                localTimePlan.value[index].endDate = newEndDate;

                // 更新选中的时间块
                selectedTimePlan.value = localTimePlan.value[index];

                // 获取容器和画布的位置信息
                const timeEcharts = canvas.closest('.time-echarts');
                if (timeEcharts) {
                    const containerRect = timeEcharts.getBoundingClientRect();
                    const canvasRect = canvas.getBoundingClientRect();

                    // 非跨天情况 - 直接计算同一天内的中点
                    const startRow = newStartRow;
                    const startHour = newStartHour + newStartMinutes / 60;
                    const endHour = newEndHour + newEndMinutes / 60;

                    // 计算水平中点（时间轴上的中点）
                    const middleX = ((startHour + endHour) / 2) * gridWidth;

                    // 非跨天的垂直位置就是该行的中点
                    const middleY = (startRow + 0.5) * rowHeight; // 加0.5表示行的中点

                    // 更新 tip 位置，使用相对于容器的位置
                    tipPosition.value = {
                        x: canvasRect.left - containerRect.left + middleX,
                        y: canvasRect.top - containerRect.top + middleY,
                    };
                }
            }
        }

        // 重绘时间块
        drawTimeBlocks();
        return;
    }

    // 处理拖拽创建新时间段的预览
    if (
        dragState.value.isDragging &&
        !dragState.value.isMovingBlock &&
        !dragState.value.isResizing
    ) {
        const moveDistance = Math.sqrt(
            Math.pow(mouseX - dragState.value.startX, 2) +
                Math.pow(mouseY - dragState.value.startY, 2),
        );

        if (moveDistance > 5) {
            dragState.value.hasMoved = true;

            const canvas = canvasRef.value;
            const timeEcharts = canvas?.closest('.time-echarts');
            if (!canvas || !timeEcharts) return;

            const canvasRect = canvas.getBoundingClientRect();
            const gridWidth = canvas.width / 24;
            const quarterWidth = gridWidth / 4;
            const blockHeight = canvas.height / days;

            // 计算开始和结束位置
            let startPosition = Math.min(dragState.value.startX, mouseX);
            let endPosition = Math.max(dragState.value.startX, mouseX);

            // 限制结束位置不超过画布宽度（24小时）
            endPosition = Math.min(endPosition, canvas.width);
            // 限制开始位置不小于0（00:00）
            startPosition = Math.max(0, startPosition);

            // 计算开始和结束行（天）
            const startRow = Math.min(
                Math.floor(dragState.value.startY / blockHeight),
                Math.floor(mouseY / blockHeight),
            );
            const endRow = Math.max(
                Math.floor(dragState.value.startY / blockHeight),
                Math.floor(mouseY / blockHeight),
            );

            // 限制行数在有效范围内
            const limitedEndRow = Math.min(endRow, days - 1);

            // 计算时间
            const startQuarters = Math.round(startPosition / quarterWidth);
            let startHour = Math.floor(startQuarters / 4);
            let startMinutes = (startQuarters % 4) * 15;

            const endQuarters = Math.round(endPosition / quarterWidth);
            let endHour = Math.floor(endQuarters / 4);
            let endMinutes = (endQuarters % 4) * 15;

            // 处理边界情况
            if (startHour < 0) {
                startHour = 0;
                startMinutes = 0;
            }

            if (endHour > 23 || (endHour === 23 && endMinutes > 59)) {
                endHour = 23;
                endMinutes = 59;
            }

            // 格式化时间
            const startDate = `${startRow + 1}-${startHour.toString().padStart(2, '0')}:${startMinutes.toString().padStart(2, '0')}`;
            const endDate = `${limitedEndRow + 1}-${endHour.toString().padStart(2, '0')}:${endMinutes.toString().padStart(2, '0')}`;

            // 更新预览 tip - 确保使用limitedEndRow（受限的行数）作为结束日期
            tipContent.value = `${startRow + 1}日-${startHour.toString().padStart(2, '0')}:${startMinutes.toString().padStart(2, '0')} - ${limitedEndRow + 1}日-${endHour.toString().padStart(2, '0')}:${endMinutes.toString().padStart(2, '0')}`;

            // 计算tip位置
            const startTotalMinutes = startRow * 24 * 60 + startHour * 60 + startMinutes;
            const endTotalMinutes = limitedEndRow * 24 * 60 + endHour * 60 + endMinutes;
            const middleTotalMinutes = (startTotalMinutes + endTotalMinutes) / 2;

            // 将中间点转换回天和小时
            const middleDay = Math.floor(middleTotalMinutes / (24 * 60));
            const middleHourMinutes = middleTotalMinutes % (24 * 60);
            const middleHour = middleHourMinutes / 60;

            // 设置tip位置
            tipPosition.value = {
                x: canvasRect.left + (middleHour % 24) * gridWidth,
                y: canvasRect.top + middleDay * blockHeight,
            };

            // 绘制预览时间块
            drawTimeBlocks(true, { startDate, endDate });
        }
        return;
    }

    // 如果没有处于拖拽状态，直接返回
    if (!dragState.value.isDragging) return;

    // 只有当鼠标真正移动了一定距离才开始绘制预览
    const moveDistance = Math.sqrt(
        Math.pow(mouseX - dragState.value.startX, 2) + Math.pow(mouseY - dragState.value.startY, 2),
    );
    if (moveDistance > 5) {
        dragState.value.hasMoved = true;
    }

    // 只有在真正移动时才绘制预览
    if (!dragState.value.hasMoved) return;

    const currentX = e.clientX - rect.left;
    const currentY = e.clientY - rect.top;
    const gridWidth = canvas.width / 24;
    const quarterWidth = gridWidth / 4;
    const rowHeight = canvas.height / days;

    // 清除画布并重新绘制所有时间块
    drawTimeBlocks();

    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    // 计算开始和结束的行
    const startRow = Math.min(
        Math.floor(dragState.value.startY / rowHeight),
        Math.floor(currentY / rowHeight),
    );
    const endRow = Math.max(
        Math.floor(dragState.value.startY / rowHeight),
        Math.floor(currentY / rowHeight),
    );

    // 计算最近的15分钟刻度位置
    const startQuarters = Math.round(Math.min(dragState.value.startX, currentX) / quarterWidth);
    const endQuarters = Math.round(Math.max(dragState.value.startX, currentX) / quarterWidth);

    // 使用与周计划相同的颜色
    ctx.fillStyle = 'rgba(29, 185, 105, 0.2)';
    const radius = 3;
    const padding = 3;

    // 判断是否是跨天选择
    if (startRow !== endRow) {
        // 跨天逻辑：处理每一行的预览效果
        for (let row = startRow; row <= endRow; row++) {
            const y = row * rowHeight;
            let rowStartX, rowEndX;

            if (row === startRow) {
                rowStartX = startQuarters * quarterWidth;
                rowEndX = canvas.width;
            } else if (row === endRow) {
                rowStartX = 0;
                rowEndX = endQuarters * quarterWidth;
            } else {
                rowStartX = 0;
                rowEndX = canvas.width;
            }

            drawRoundedRect(
                ctx,
                rowStartX,
                y + padding,
                rowEndX - rowStartX,
                rowHeight - padding * 2,
                radius,
            );
            ctx.fill();
        }
    } else {
        // 非跨天逻辑：单个时间块
        const startX = startQuarters * quarterWidth;
        const endX = endQuarters * quarterWidth;
        const width = endX - startX;
        const y = startRow * rowHeight;

        drawRoundedRect(ctx, startX, y + padding, width, rowHeight - padding * 2, radius);
        ctx.fill();
    }

    // 更新预览 tip
    const startHour = Math.floor(startQuarters / 4);
    const startMinutes = (startQuarters % 4) * 15;
    const endHour = Math.floor(endQuarters / 4);
    const endMinutes = (endQuarters % 4) * 15;

    const startDay = startRow + 1;
    const endDay = endRow + 1;

    // 限制结束行不超过最大天数
    const limitedEndDay = Math.min(endDay, days);

    // 显示预览 tip - 使用经过限制的结束日期
    tipContent.value = `${startDay}日-${startHour.toString().padStart(2, '0')}:${startMinutes.toString().padStart(2, '0')} - ${limitedEndDay}日-${endHour.toString().padStart(2, '0')}:${endMinutes.toString().padStart(2, '0')}`;

    // 计算 tip 位置
    const tipX = ((startQuarters + endQuarters) / 2) * quarterWidth;
    const tipY = startRow * rowHeight;

    // 设置 tip 位置
    tipPosition.value = {
        x: rect.left + tipX,
        y: rect.top + tipY,
    };
};

const handleMouseUp = (e: MouseEvent) => {
    // 如果是查看模式，完全禁止任何交互，直接返回
    if (props.isViewMonthPlan) {
        return;
    }

    // 移除 user-select: none
    document.body.style.userSelect = '';

    if (dragState.value.isResizing) {
        dragState.value.isResizing = false;
        dragState.value.resizeDirection = null;
        emit('timeSelect', localTimePlan.value);

        // 更新提示位置，确保立即显示
        updateTip();
        // 在下一帧再观察选中的时间块，确保DOM已更新
        nextTick(() => {
            observeSelectedTimeBlock();
        });

        // 保持选中状态和可移动状态
        dragState.value.canMoveBlock = true;
        // 重置 clickedPlan
        dragState.value.clickedPlan = null;
        return;
    }

    if (dragState.value.isMovingBlock) {
        dragState.value.isMovingBlock = false;
        emit('timeSelect', localTimePlan.value);

        // 更新提示位置，确保立即显示
        updateTip();
        // 在下一帧再观察选中的时间块
        nextTick(() => {
            observeSelectedTimeBlock();
        });

        // 保持选中状态和可移动状态
        dragState.value.canMoveBlock = true;
        // 重置 clickedPlan
        dragState.value.clickedPlan = null;
        return;
    }

    const canvas = canvasRef.value;
    const rect = canvas?.getBoundingClientRect();
    if (!canvas || !rect) return;

    const clickX = e.clientX - rect.left;
    const clickY = e.clientY - rect.top;

    // 检查鼠标是否在画布内
    const isOutsideCanvas =
        e.clientX < rect.left ||
        e.clientX > rect.right ||
        e.clientY < rect.top ||
        e.clientY > rect.bottom;

    // 不使用缓存的 clickedPlan，而是直接根据当前点击位置检测
    const clickedPlan = isOutsideCanvas ? null : findClickedTimePlan(clickX, clickY);

    // 这里是关键：用于处理点击选中时间块
    // 只有当是点击操作（没有移动）且dragState.isDragging为true时，才进行选中逻辑
    if (dragState.value.isDragging && !dragState.value.hasMoved) {
        if (clickedPlan) {
            // 选中时间块
            selectedTimePlan.value = clickedPlan;
            dragState.value.canMoveBlock = true;

            // 设置为可见状态并立即更新提示位置
            isSelectedBlockVisible.value = true;
            updateTip();

            // 在下一帧再观察选中的时间块
            nextTick(() => {
                observeSelectedTimeBlock();
            });
        } else {
            // 点击空白处或画布外，取消选中
            selectedTimePlan.value = null;
            dragState.value.canMoveBlock = false;
            resetTipPosition(); // 清空提示内容
        }
        drawTimeBlocks();
        dragState.value.isDragging = false;
        dragState.value.clickedPlan = null;
        return;
    }

    // 如果是拖拽操作（isDragging && hasMoved），继续执行创建新时间段的逻辑
    if (dragState.value.isDragging && dragState.value.hasMoved) {
        const gridWidth = canvas.width / 24;
        const quarterWidth = gridWidth / 4;
        const rowHeight = canvas.height / days;

        // 计算开始时间
        let startPosition = Math.min(dragState.value.startX, clickX);
        // 限制开始位置不小于0（00:00）
        startPosition = Math.max(0, startPosition);
        const startQuarters = Math.round(startPosition / quarterWidth);
        let startHour = Math.floor(startQuarters / 4);
        let startMinutes = (startQuarters % 4) * 15;

        // A处理开始时间边界情况
        if (startHour < 0) {
            startHour = 0;
            startMinutes = 0;
        }

        // 计算结束时间
        let endPosition = Math.max(dragState.value.startX, clickX);
        // 限制结束位置不超过画布宽度（24小时）
        endPosition = Math.min(endPosition, canvas.width);
        const endQuarters = Math.round(endPosition / quarterWidth);
        let endHour = Math.floor(endQuarters / 4);
        let endMinutes = (endQuarters % 4) * 15;

        // 处理结束时间边界情况
        if (endHour > 23 || (endHour === 23 && endMinutes > 59)) {
            endHour = 23;
            endMinutes = 59;
        }

        // 计算开始和结束的行（月份中的第几天）
        const startRow = Math.min(
            Math.floor(dragState.value.startY / rowHeight),
            Math.floor(clickY / rowHeight),
        );
        const endRow = Math.max(
            Math.floor(dragState.value.startY / rowHeight),
            Math.floor(clickY / rowHeight),
        );

        // 创建跨天的时间段
        const startDay = startRow + 1;
        const endDay = endRow + 1;
        let startDate = `${startDay}-${startHour.toString().padStart(2, '0')}:${startMinutes.toString().padStart(2, '0')}`;
        let endDate = `${endDay}-${endHour.toString().padStart(2, '0')}:${endMinutes.toString().padStart(2, '0')}`;

        // 检查是否是有效的时间段（开始时间早于结束时间）
        const startTime = parseTime(startDate);
        const endTime = parseTime(endDate);
        if (!startTime || !endTime) return;

        // 如果是同一天，检查开始时间是否早于结束时间
        if (
            startDay === endDay &&
            (startHour > endHour || (startHour === endHour && startMinutes >= endMinutes))
        ) {
            // 无效的时间段，不创建
            dragState.value.isDragging = false;
            dragState.value.hasMoved = false;
            dragState.value.startX = 0;
            dragState.value.startY = 0;
            dragState.value.startRow = 0;
            dragState.value.clickedPlan = null;
            drawTimeBlocks();
            return;
        }

        // 添加新的时间段到本地数组
        const newPlan = { startDate, endDate };
        localTimePlan.value.push(newPlan);

        // 发送事件到父组件
        emit('timeSelect', localTimePlan.value);
    }

    // 重置拖拽状态
    dragState.value.isDragging = false;
    dragState.value.hasMoved = false;
    dragState.value.startX = 0;
    dragState.value.startY = 0;
    dragState.value.startRow = 0;
    dragState.value.clickedPlan = null;

    drawTimeBlocks();
};

// 修改 handleKeyDown 函数，使用更可靠的方式查找时间块
const handleKeyDown = (e: KeyboardEvent) => {
    // 如果有选中的时间块，并且按下了 Backspace 或 Delete 键
    if ((e.key === 'Backspace' || e.key === 'Delete') && selectedTimePlan.value) {
        // 确保删除后至少还有一条数据
        if (localTimePlan.value.length > 1) {
            // 使用更可靠的方式查找选中的时间块
            const index = localTimePlan.value.findIndex(
                (plan) =>
                    plan.startDate === selectedTimePlan.value?.startDate &&
                    plan.endDate === selectedTimePlan.value?.endDate,
            );

            if (index !== -1) {
                // 删除时间块
                localTimePlan.value.splice(index, 1);
                // 清除选中状态
                selectedTimePlan.value = null;
                dragState.value.canMoveBlock = false;
                // 重新绘制时间块
                drawTimeBlocks();
                // 发送更新事件到父组件
                emit('timeSelect', localTimePlan.value);
            } else {
                // 打印数组内容以便调试
            }
        }
        // 阻止默认行为（如浏览器的后退）
        e.preventDefault();
    }
};

// 添加一个处理document点击事件的函数，用于在点击非时间块区域时取消选中
const handleDocumentClick = (e: MouseEvent) => {
    // 如果当前没有选中时间块，或者正在调整大小、移动或拖拽，不做处理
    if (
        !selectedTimePlan.value ||
        dragState.value.isResizing ||
        dragState.value.isMovingBlock ||
        dragState.value.isDragging
    ) {
        return;
    }

    const canvas = canvasRef.value;
    if (!canvas) return;

    // 检查点击是否发生在画布上
    const rect = canvas.getBoundingClientRect();
    const isClickOnCanvas =
        e.clientX >= rect.left &&
        e.clientX <= rect.right &&
        e.clientY >= rect.top &&
        e.clientY <= rect.bottom;

    if (isClickOnCanvas) {
        // 如果点击在画布上，我们需要检查是否点击在时间块上
        const clickX = e.clientX - rect.left;
        const clickY = e.clientY - rect.top;
        const clickedPlan = findClickedTimePlan(clickX, clickY);

        // 如果点击的不是当前选中的时间块，则取消选中
        if (
            !clickedPlan ||
            clickedPlan.startDate !== selectedTimePlan.value.startDate ||
            clickedPlan.endDate !== selectedTimePlan.value.endDate
        ) {
            resetTipPosition();
            selectedTimePlan.value = null;
            dragState.value.canMoveBlock = false;
            drawTimeBlocks();
        }

        return;
    }

    // 点击在画布外，取消选中
    resetTipPosition();
    selectedTimePlan.value = null;
    dragState.value.canMoveBlock = false;
    drawTimeBlocks();
};

// 添加 tip 相关的响应式数据
const tipContent = ref('');
const tipPosition = ref({ x: 0, y: 0 });

// 添加滚动事件处理函数
const handleScroll = () => {
    if (selectedTimePlan.value) {
        // 检查选中时间块的可见性
        if (!isSelectedBlockVisible.value) {
            resetTipPosition();
            selectedTimePlan.value = null;
            dragState.value.canMoveBlock = false;
            drawTimeBlocks();
            return;
        }

        // 如果时间块可见，则更新tip位置
        if (isSelectedBlockVisible.value) {
            // 使用requestAnimationFrame确保在下一次重绘前更新位置
            requestAnimationFrame(() => {
                updateTip();
            });
        }

        // 滚动时重新检查可见性
        observeSelectedTimeBlock();
    }
};

// 添加一个工具函数来计算总分钟数
const calculateTotalMinutes = (day: number, hours: number, minutes: number): number => {
    return (day - 1) * 24 * 60 + hours * 60 + minutes;
};

// 添加一个isSelectedBlockVisible状态来跟踪选中的时间块是否可见
const isSelectedBlockVisible = ref(true);

// 添加一个观察者变量
let selectedBlockObserver = null;

// 添加重置提示位置的函数
const resetTipPosition = () => {
    tipContent.value = '';
    tipPosition.value = { x: 0, y: 0 };
};

// 修改 updateTip 函数，确保考虑滚动位置
const updateTip = () => {
    if (!selectedTimePlan.value) {
        resetTipPosition();
        return;
    }

    // 更新 tip 的 z-index 值
    updateTipZIndex();

    const startTime = parseTime(selectedTimePlan.value.startDate);
    const endTime = parseTime(selectedTimePlan.value.endDate);
    if (!startTime || !endTime) {
        return;
    }

    // 确保结束日期不超过最大天数
    const limitedEndDay = Math.min(parseInt(endTime.day), days);

    // 设置tip内容，确保格式一致并限制结束日期不超过最大值
    tipContent.value = `${startTime.day}号-${String(startTime.hours).padStart(2, '0')}:${String(startTime.minutes).padStart(2, '0')} - ${limitedEndDay}号-${String(endTime.hours).padStart(2, '0')}:${String(endTime.minutes).padStart(2, '0')}`;

    // 确保在下一帧绘制完成后再计算位置
    requestAnimationFrame(() => {
        const canvas = canvasRef.value;
        if (!canvas) {
            return;
        }

        // 获取最新的画布位置信息
        const canvasRect = canvas.getBoundingClientRect();

        // 计算时间块的位置
        const gridWidth = canvas.width / 24;
        const rowHeight = canvas.height / days;

        // 检查是否为跨天时间块
        const isCrossDay = startTime.day !== endTime.day;

        let middleX, middleY;

        if (isCrossDay) {
            // 跨天情况 - 使用开始和结束时间的中点
            // 解析开始和结束时间为分钟数
            const startTotalMinutes = calculateTotalMinutes(
                parseInt(startTime.day),
                startTime.hours,
                startTime.minutes,
            );

            const endTotalMinutes = calculateTotalMinutes(
                parseInt(endTime.day),
                endTime.hours,
                endTime.minutes,
            );

            // 计算中间点的分钟数
            const middleTotalMinutes = (startTotalMinutes + endTotalMinutes) / 2;

            // 将中间点的分钟数转换回天和小时
            const middleDay = Math.floor(middleTotalMinutes / (24 * 60));
            const middleHourMinutes = middleTotalMinutes % (24 * 60);
            const middleHour = middleHourMinutes / 60;

            // 计算中间点在画布上的位置（像素坐标）
            middleX = (middleHour % 24) * gridWidth;
            middleY = middleDay * rowHeight;
        } else {
            // 非跨天情况 - 直接计算同一天内的中点
            const startRow = parseInt(startTime.day) - 1;
            const startHour = startTime.hours + startTime.minutes / 60;
            const endHour = endTime.hours + endTime.minutes / 60;

            // 计算水平中点（时间轴上的中点）
            middleX = ((startHour + endHour) / 2) * gridWidth;

            // 非跨天的垂直位置就是该行的中点
            middleY = (startRow + 0.5) * rowHeight; // 加0.5表示行的中点
        }

        // 设置 tip 位置，确保考虑滚动位置
        tipPosition.value = {
            x: canvasRect.left + middleX,
            y: canvasRect.top + middleY - 10, // 上移10px给箭头留空间
        };
    });
};

// 修改观察选中时间块的函数，实现当时间块不可见时自动取消选中
const observeSelectedTimeBlock = () => {
    if (!selectedTimePlan.value) {
        isSelectedBlockVisible.value = false;
        return;
    }

    // 默认设置为可见状态
    isSelectedBlockVisible.value = true;

    const canvas = canvasRef.value;
    if (!canvas) return;

    // 获取画布所在的容器元素
    const container = canvas.closest('.time-echarts');
    if (!container) return;

    // 创建或获取临时DOM元素来代表选中的时间块
    let blockElement = document.getElementById('selected-time-block-tracker');
    if (!blockElement) {
        blockElement = document.createElement('div');
        blockElement.id = 'selected-time-block-tracker';
        blockElement.style.position = 'absolute';
        blockElement.style.zIndex = '1000';
        blockElement.style.background = 'transparent';
        blockElement.style.pointerEvents = 'none';

        // 为了调试可以临时设置一个背景色
        // blockElement.style.background = 'rgba(255, 0, 0, 0.2)';

        // 添加到容器内部而不是body，这样可以保持正确的相对定位
        container.appendChild(blockElement);
    }

    const startTime = parseTime(selectedTimePlan.value.startDate);
    const endTime = parseTime(selectedTimePlan.value.endDate);
    if (!startTime || !endTime) return;

    const gridWidth = canvas.width / 24;
    const rowHeight = canvas.height / days;

    // 计算时间块的位置和大小
    const startRow = parseInt(startTime.day) - 1;
    const endRow = parseInt(endTime.day) - 1;
    const startHour = startTime.hours + startTime.minutes / 60;
    const endHour = endTime.hours + endTime.minutes / 60;

    // 设置时间块的位置和大小（相对于容器）
    // 注意：这里使用相对于容器的位置，不使用绝对屏幕位置
    blockElement.style.left = `${startHour * gridWidth}px`;
    blockElement.style.top = `${startRow * rowHeight}px`;
    blockElement.style.width = `${(endRow > startRow ? 24 - startHour : endHour - startHour) * gridWidth}px`;
    blockElement.style.height = `${rowHeight * (endRow - startRow + 1)}px`;

    // 确保元素可见（用于调试）
    blockElement.style.display = 'block';

    // 清除旧的观察器
    if (selectedBlockObserver) {
        try {
            if (typeof selectedBlockObserver.disconnect === 'function') {
                selectedBlockObserver.disconnect();
            }
        } catch (e) {
            // 忽略可能的错误
        }
    }

    try {
        // 创建新的IntersectionObserver
        // 使用视窗作为root，这样我们检测的是元素是否在视窗内可见
        const observer = new IntersectionObserver(
            (entries) => {
                const [entry] = entries;
                const isVisible = entry.isIntersecting;

                // 更新可见性状态
                isSelectedBlockVisible.value = isVisible;

                // 可见性状态改变时更新tip位置
                if (isVisible) {
                    // 如果时间块可见，更新tip位置
                    updateTip();
                } else if (selectedTimePlan.value) {
                    // 如果时间块完全不可见（整个块都离开了视窗），并且不是在交互过程中，
                    // 则取消选中状态
                    if (
                        !dragState.value.isDragging &&
                        !dragState.value.isMovingBlock &&
                        !dragState.value.isResizing &&
                        !dragState.value.hasMoved // 确保不是刚点击的情况
                    ) {
                        // 重要：在取消选中之前重置tip位置
                        resetTipPosition();

                        // 取消选中状态
                        selectedTimePlan.value = null;
                        dragState.value.canMoveBlock = false;

                        // 重绘时间块
                        drawTimeBlocks();
                    }
                }
            },
            {
                // 使用null作为root表示使用视窗作为参考
                root: null,
                // 增加阈值，只有当元素完全不可见时才触发不可见状态
                threshold: 0.01,
                // 使用边距来调整可见性判断的精度
                rootMargin: '0px',
            },
        );

        // 开始观察
        observer.observe(blockElement);

        // 保存观察器引用
        selectedBlockObserver = observer;
    } catch (e) {
        // 忽略可能的错误，确保不会阻止选中功能
        console.error('Error setting up intersection observer:', e);
    }
};

// 修改选中时间块变化的处理，始终确保updateTip被调用
watch(
    () => selectedTimePlan.value,
    (newVal) => {
        if (newVal) {
            // 当选中新的时间块时，立即更新tip位置
            updateTip();
            // 启动可见性观察
            observeSelectedTimeBlock();
        } else {
            // 重置tip位置
            resetTipPosition();

            // 安全地清理观察器
            if (selectedBlockObserver) {
                try {
                    if (typeof selectedBlockObserver.disconnect === 'function') {
                        selectedBlockObserver.disconnect();
                    }
                } catch (e) {
                    // 忽略可能的错误
                }
            }
        }
    },
    { deep: true },
);

// 更新 getCursorStyle 函数以提供更明确的视觉反馈
const getCursorStyle = () => {
    // 如果是查看模式，显示默认光标表示不可交互
    if (props.isViewMonthPlan) {
        return 'default';
    }

    // 判断拖拽状态
    if (dragState.value.isDragging) {
        return 'col-resize';
    }

    // 检查鼠标是否悬停在调整大小区域
    if (selectedTimePlan.value) {
        return 'pointer';
    }

    return 'pointer';
};

onMounted(() => {
    if (!Array.isArray(props.timePlan)) {
        smallestUnitMonth.value = 0;
        return;
    }

    // 检测是否支持 Teleport/Portal
    if (!isVue2) {
        // Vue 3 环境使用 Teleport
        portalComponent.value = 'teleport';
        hasPortalSupport.value = true;
    } else {
        // 检查是否有 Portal 插件
        const vueVersion = (window as any).Vue;
        const hasPortal =
            (vueVersion && vueVersion.component && vueVersion.component('portal')) ||
            document.querySelector('[data-portal], [data-teleport]') !== null;
        if (hasPortal) {
            portalComponent.value = 'portal';
            hasPortalSupport.value = true;
        } else {
            // 不支持传送门，使用回退方案
            hasPortalSupport.value = false;
        }
    }

    // 更新 tip z-index
    updateTipZIndex();

    calculateCellWidth();

    const resizeObserver = new ResizeObserver(() => {
        calculateCellWidth();
        drawTimeBlocks();
        // 在容器大小变化时更新 tip 位置
        updateTip();
    });

    if (rightMonthRef.value) {
        resizeObserver.observe(rightMonthRef.value);
        // 同时监听整个窗口的大小变化
        window.addEventListener('resize', updateTip);

        const canvas = canvasRef.value;
        if (canvas) {
            canvas.addEventListener('mousedown', handleMouseDown);
            document.addEventListener('mousemove', handleMouseMove);
            document.addEventListener('mouseup', handleMouseUp);
            document.addEventListener('keydown', handleKeyDown);
            // 添加 document 点击事件处理
            document.addEventListener('click', handleDocumentClick);
        }
    }

    // 设置月份天数的 CSS 变量
    document.documentElement.style.setProperty('--month-days', days.toString());

    // 添加滚动事件监听
    window.addEventListener('scroll', handleScroll);
});

onBeforeUnmount(() => {
    if (resizeObserver && rightMonthRef.value) {
        try {
            if (typeof resizeObserver.unobserve === 'function') {
                resizeObserver.unobserve(rightMonthRef.value);
            }
        } catch (e) {
            // 忽略可能的错误
        }
    }
    document.removeEventListener('mousemove', handleMouseMove);
    document.removeEventListener('mouseup', handleMouseUp);
    document.removeEventListener('keydown', handleKeyDown);
    // 移除 document 点击事件处理
    document.removeEventListener('click', handleDocumentClick);
    // 移除窗口大小变化监听
    window.removeEventListener('resize', updateTip);
    window.removeEventListener('scroll', handleScroll);

    const canvas = canvasRef.value;
    if (canvas) {
        canvas.removeEventListener('mousedown', handleMouseDown);
    }

    // 清理滚动监听和observer
    if (selectedBlockObserver) {
        try {
            if (typeof selectedBlockObserver.disconnect === 'function') {
                selectedBlockObserver.disconnect();
            }
        } catch (e) {
            // 忽略可能的错误
        }
    }

    // 移除临时DOM元素
    const blockElement = document.getElementById('selected-time-block-tracker');
    if (blockElement && blockElement.parentNode) {
        blockElement.parentNode.removeChild(blockElement);
    }
});
</script>

<style scoped lang="less">
/* 
 * MonthPlan组件内部样式
 * 这些样式仅应用于当前组件内的元素，不会影响组件外部 
 */
.time-echarts {
    @border-color: #cccccc;
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-bottom: 20px;

    .contents-month {
        width: 100%;
        margin-bottom: 20px;
    }

    .ul-month {
        display: flex;
        margin-left: 40px;
        flex-grow: 1;
        flex-shrink: 0;

        padding: 0;
        li {
            flex: 1;
            height: 24px;
            text-align: center;
            line-height: 23px;
            box-sizing: border-box;
            font-size: 11px;
            list-style: none;
            padding: 0;
            color: #7e8494;
            &:last-child {
                margin-right: 1px;
            }
        }
    }

    .bottom {
        display: flex;
    }

    .right-month {
        width: 100%;
        position: relative;
        background-color: #fff;
        border-top: 1px solid @border-color;
        box-sizing: border-box;

        .grid-container {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            display: grid;
            grid-template-columns: repeat(24, 1fr);
            grid-template-rows: repeat(var(--month-days), 1fr);
            pointer-events: none;
            border-left: 1px solid @border-color;

            .grid-column {
                border-right: 1px solid #cccccc;
                border-bottom: 1px solid #cccccc;
            }
        }

        canvas {
            position: absolute;
            top: 0;
            left: 0;
            height: 100%;
            width: 100%;
            pointer-events: auto;
        }
    }

    .monthday {
        font-size: 11px;
        background-color: #fff;
        width: 40px;
        text-align: center;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        padding: 0 4px;
        color: #7e8494;
        box-sizing: border-box;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 1px;

        .day-number {
            font-size: 12px;
        }

        .day-unit {
            font-size: 10px;
            color: #666;
        }
    }

    .tip-container {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        pointer-events: none;
        z-index: 1000;
    }
}
</style>

<style lang="less">
/* 
 * 全局样式，用于Teleport/Portal传送到body的元素
 * 这些样式会应用到整个应用程序，因此使用特定前缀避免影响其他组件
 * 必须使用非scoped样式，因为传送门内容在组件DOM树之外
 */
.time-tip {
    position: fixed; /* 保持fixed定位，但在计算位置时考虑滚动位置 */
    background: linear-gradient(to bottom, #2c3e50, #34495e);
    color: white;
    padding: 5px 10px;
    border-radius: 4px;
    line-height: normal;
    font-size: 12px;
    pointer-events: none;
    white-space: nowrap;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
    letter-spacing: 0.5px;
    font-weight: 500;
    border: 1px solid rgba(255, 255, 255, 0.1);
    z-index: 9999;
    will-change: transform; /* 启用硬件加速，提高滚动性能 */
    /* 移除transform使其与position计算一致 */

    &::after {
        content: '';
        position: absolute;
        left: 50%;
        bottom: -5px;
        transform: translateX(-50%);
        border-left: 5px solid transparent;
        border-right: 5px solid transparent;
        border-top: 5px solid #34495e;
        filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.1));
    }

    animation: tipFadeIn 0.2s ease-out;
}

/* 为全局样式添加前缀，避免与其他组件冲突 */
@keyframes tipFadeIn {
    from {
        opacity: 0;
        transform: translateY(-10px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}
</style>
